Skip to content

28 inspect自省

你想知道一个函数有哪些参数、一个类有哪些方法、一段代码的源码是什么——这些在运行时动态检查对象的能力叫"自省"(Introspection)。inspect模块就是干这个的。

一、类型检查

1.1 常用检查函数

python
import inspect

# 检查类型
inspect.isclass(int)              # True
inspect.isfunction(lambda: None)  # True
inspect.ismethod(str.upper)       # True
inspect.ismodule(inspect)         # True

# 检查是否为协程函数
async def async_func(): pass
inspect.iscoroutinefunction(async_func)  # True

# 检查是否为生成器函数
def gen():
    yield 1
inspect.isgeneratorfunction(gen)  # True

1.2 完整类型检查列表

python
import inspect

obj = "hello"

inspect.isclass(type(obj))      # True
inspect.isfunction(obj)         # False
inspect.ismethod(obj)           # False
inspect.ismodule(obj)           # False
inspect.isbuiltin(len)          # True
inspect.isroutine(obj)          # False(函数或方法)

二、获取成员

2.1 getmembers()

python
import inspect

# 获取对象的所有成员
members = inspect.getmembers(str)
for name, value in members[:5]:
    print(f"{name}: {type(value).__name__}")

2.2 带过滤条件

python
import inspect

# 只获取方法
methods = inspect.getmembers(str, predicate=inspect.ismethod)
print(methods[:5])

# 只获取函数
functions = inspect.getmembers(inspect, predicate=inspect.isfunction)
print(functions[:5])

三、函数签名

3.1 获取签名

python
import inspect

def example(a: int, b: str = "hello", *args, **kwargs) -> bool:
    pass

sig = inspect.signature(example)
print(sig)  # (a: int, b: str = 'hello', *args, **kwargs) -> bool

3.2 分析参数

python
import inspect

def example(a: int, b: str = "hello", *args, c: float = 1.0, **kwargs) -> bool:
    pass

sig = inspect.signature(example)

for name, param in sig.parameters.items():
    print(f"参数: {name}")
    print(f"  类型注解: {param.annotation}")
    print(f"  默认值: {param.default}")
    print(f"  种类: {param.kind}")

参数种类:

种类说明
POSITIONAL_ONLY仅位置参数
POSITIONAL_OR_KEYWORD位置或关键字参数(默认)
VAR_POSITIONAL*args
KEYWORD_ONLY仅关键字参数
VAR_KEYWORD**kwargs

3.3 绑定参数

python
import inspect

def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

sig = inspect.signature(greet)

# 绑定参数
bound = sig.bind("World")
print(bound.arguments)  # {'name': 'World'}

# 带默认值
bound = sig.bind("World", "Hi")
print(bound.arguments)  # {'name': 'World', 'greeting': 'Hi'}

# 关键字参数
bound = sig.bind(name="World", greeting="Hey")
print(bound.arguments)  # {'name': 'World', 'greeting': 'Hey'}

四、获取源代码

4.1 getsource()

python
import inspect

def hello():
    """打招呼"""
    print("Hello, World!")

# 获取源代码
source = inspect.getsource(hello)
print(source)

4.2 getsourcelines()

python
import inspect

def hello():
    print("Hello")

# 获取源代码和起始行号
lines, lineno = inspect.getsourcelines(hello)
print(f"起始行: {lineno}")
print(f"代码: {lines}")

4.3 getfile()

python
import inspect

# 获取定义位置
print(inspect.getfile(inspect))  # /usr/lib/python3.x/inspect.py

五、调用栈检查

5.1 stack()

python
import inspect

def a():
    b()

def b():
    c()

def c():
    # 获取调用栈
    frame_stack = inspect.stack()
    for frame_info in frame_stack:
        print(f"函数: {frame_info.function}, 行: {frame_info.lineno}")

a()

5.2 currentframe()

python
import inspect

def example():
    frame = inspect.currentframe()
    print(f"当前函数: {frame.f_code.co_name}")
    print(f"调用者: {frame.f_back.f_code.co_name}")

def caller():
    example()

caller()

5.3 getframeinfo()

python
import inspect

def example():
    frame = inspect.currentframe()
    info = inspect.getframeinfo(frame)
    print(f"文件: {info.filename}")
    print(f"行号: {info.lineno}")
    print(f"函数: {info.function}")
    print(f"代码: {info.code_context}")

六、类检查

6.1 获取类的方法

python
import inspect

class MyClass:
    def method1(self):
        pass

    def method2(self):
        pass

# 获取所有方法
methods = inspect.getmembers(MyClass, predicate=inspect.isfunction)
for name, func in methods:
    print(f"方法: {name}")

6.2 获取方法签名

python
import inspect

class Calculator:
    def add(self, a: int, b: int) -> int:
        return a + b

sig = inspect.signature(Calculator.add)
print(sig)  # (self, a: int, b: int) -> int

# 获取参数(排除self)
params = {k: v for k, v in sig.parameters.items() if k != 'self'}
print(params)

七、模块检查

7.1 获取模块信息

python
import inspect

# 获取模块成员
members = inspect.getmembers(inspect)

# 获取模块中的类
classes = inspect.getmembers(inspect, inspect.isclass)
print(f"inspect模块有 {len(classes)} 个类")

# 获取模块中的函数
functions = inspect.getmembers(inspect, inspect.isfunction)
print(f"inspect模块有 {len(functions)} 个函数")

八、实战场景

8.1 自动文档生成

python
import inspect

def generate_docs(func):
    """生成函数文档"""
    sig = inspect.signature(func)
    doc = func.__doc__ or "无文档"

    lines = [f"函数: {func.__name__}"]
    lines.append(f"签名: {sig}")
    lines.append(f"文档: {doc}")
    lines.append("参数:")

    for name, param in sig.parameters.items():
        default = f" = {param.default}" if param.default != inspect.Parameter.empty else ""
        annotation = f": {param.annotation}" if param.annotation != inspect.Parameter.empty else ""
        lines.append(f"  {name}{annotation}{default}")

    return "\n".join(lines)

def example(a: int, b: str = "hello") -> bool:
    """示例函数"""
    return True

print(generate_docs(example))

8.2 参数验证

python
import inspect

def validate_args(func, *args, **kwargs):
    """验证参数"""
    sig = inspect.signature(func)
    try:
        sig.bind(*args, **kwargs)
        return True
    except TypeError as e:
        print(f"参数错误: {e}")
        return False

def add(a: int, b: int) -> int:
    return a + b

validate_args(add, 1, 2)       # True
validate_args(add, 1)          # False: 缺少参数
validate_args(add, 1, 2, 3)    # False: 参数过多

8.3 装饰器中获取原函数信息

python
import inspect

def my_decorator(func):
    # 保留原函数的签名
    sig = inspect.signature(func)

    def wrapper(*args, **kwargs):
        # 验证参数
        sig.bind(*args, **kwargs)
        return func(*args, **kwargs)

    wrapper.__signature__ = sig
    wrapper.__name__ = func.__name__
    return wrapper

@my_decorator
def greet(name: str, greeting: str = "Hello") -> str:
    return f"{greeting}, {name}!"

# 装饰后的函数保留了原签名
print(inspect.signature(greet))  # (name: str, greeting: str = 'Hello') -> str

8.4 调试信息

python
import inspect

def debug_call(func):
    def wrapper(*args, **kwargs):
        frame = inspect.currentframe().f_back
        print(f"调用 {func.__name__}()")
        print(f"  来自 {frame.f_code.co_name}:{frame.f_lineno}")
        result = func(*args, **kwargs)
        print(f"  返回 {result}")
        return result
    return wrapper

@debug_call
def add(a, b):
    return a + b

add(1, 2)

九、总结

inspect模块的核心:

函数用途
isclass/isfunction/ismethod类型检查
getmembers()获取对象成员
signature()获取函数签名
getsource()获取源代码
stack()/currentframe()调用栈检查
getframeinfo()帧信息

使用场景:

  • 自动文档生成
  • 参数验证
  • 调试和日志
  • 元编程
  • 装饰器中保留原函数信息

inspect是Python元编程的基础,写框架、写装饰器时特别有用。